package network;

import java.beans.PropertyChangeSupport;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Random;

import util.MD5Converter;
import util.NetworkCode;

import android.util.Log;

public class AstroClient {
	
	private static AstroClient instance;
	private boolean isConnected;
	private ArrayList<String> log;
	private Thread clientThread;
	private Thread senderThread;
	private Sender send;
	private Reciever reciever;
	private int money = 0;
	private Socket clientSocket;
	private ArrayList<String> latestLogSent;
	private PropertyChangeSupport pcs;
	private boolean getAuthenticated;
	private boolean isAuthenticated;
	private String md5PassKey;
	private boolean shouldRun = true;
	public long portFolioValue = 0;

	
	
	
	public AstroClient(){
		isConnected = false;
		reciever = new Reciever();
		log = new ArrayList<String>();
		send = new Sender();
		getAuthenticated = false;
	}
	
	@SuppressWarnings("unchecked")
	public ArrayList<String> getLatestLog(){
		return (ArrayList<String>) log.clone();
	}
	
	public Long getPortfolioValue(){
		return portFolioValue;
	}
	
	public void clearLog(){
		log.removeAll(latestLogSent);
	}
	
	public void setMoney(int money) {
		this.money  = money;
	}
	
	public int getMoney(){
		return money;
	}	

	
	public boolean isConnected(){
		return isConnected;
	}
	
	private void disconnected(){
		isConnected = false;
		//Push disconnected to GUI.
	}

	public boolean connect(String hostname, int portNumber, PropertyChangeSupport pcs, String password) {
		log.clear();
		Log.i("Nework", "Connecting to " + hostname + " with Password: " + password);
		if(reciever.connect(hostname, portNumber)){
			isAuthenticated = false;
			clientThread = new Thread(reciever);
			senderThread = new Thread(send);
			this.md5PassKey = MD5Converter.toMD5(password);
			clientThread.start();
			senderThread.start();
			getAuthenticated = true;
			this.pcs = pcs;
			return true;
		}
		return false;
		
	}

	public Boolean disconnect() {
		isAuthenticated = false;
		getAuthenticated = false;
		isConnected = false;
		reciever.shutdown();
		try {
			clientSocket.close();
		} catch (IOException e) {;
		}
		while(clientThread.isAlive() );
		return true;
	}
	
	private class Reciever implements Runnable {
		private BufferedReader inputReader;
		
		public void shutdown(){
			shouldRun = false;
		}
		
		public boolean connect(String hostname, int portNumber){
			try {
				Log.i("Network", "Connecting to: " + hostname + ":" + portNumber);
				clientSocket = new Socket();
				SocketAddress sockaddr = new InetSocketAddress(hostname, portNumber);
				clientSocket.connect(sockaddr, 3500);
				isConnected = true;
				inputReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
				return true;
			} catch (UnknownHostException e) {
				return false;
			} catch (IOException e) {
				return false;
			}
		}
		public void run() {
			while(shouldRun){ 	
				while(isConnected && shouldRun){
					try {	
						if(inputReader.ready()){
							String strInput = inputReader.readLine();
							handleInput(strInput);
						}
					} catch (IOException e) {
						e.printStackTrace();
						disconnected();
					}
				}
				while(!isConnected && shouldRun){
					try {
						Thread.sleep(200);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}

			}
		}

		private void handleInput(String strInput) {
			Log.i("Network", "Recieved input from ASTRo. :" + strInput);
			if(strInput.startsWith(NetworkCode.LogEvent)){
				Log.i("Network", "Recieved Log event from ASTRo.");
				newLogEvent(strInput.substring(2));
			} else if(strInput.startsWith(NetworkCode.PortfolioValue)){
				Log.i("Network", "Recieved Portfolio-value from ASTRo.");
				setPortfolioValue(Long.parseLong(strInput.substring(2)));
			} else if(strInput.startsWith(NetworkCode.Authenticated)){
				Log.i("Network", "Has been authenticated by ASTRo Server.");
				isAuthenticated = true;
			} else if(strInput.startsWith(NetworkCode.NotAuthenticated)){
				Log.i("Network", "Has NOT been authenticated by ASTRo Server.");
				isAuthenticated = false;
			}
		}

		private void newLogEvent(String msg) {
			pcs.firePropertyChange("LogItem", null, msg);
			log.add(msg);
		}
		

		private void setPortfolioValue(long newValue) {
			pcs.firePropertyChange("PortfolioValueChanged", null, newValue);
			portFolioValue  = newValue;
		}

		


	}
	
	private class Sender implements Runnable {		
		public void run() {
			while(shouldRun){ 
				while(isConnected) {
					if(getAuthenticated){
						try {
					        PrintWriter output = new PrintWriter(clientSocket.getOutputStream());
					        output.println(NetworkCode.Key + md5PassKey);
					        output.flush();
					        getAuthenticated = false;
						} catch (IOException e) {
							e.printStackTrace();
							disconnected();
						}
					}
				}
			}
		}
		
	}

	public boolean isAuthenticated() {
		return isAuthenticated;
	}



	
	
}
